package com.zhixinglu.spring.boot.generator.nickname.utils;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.concurrent.ThreadLocalRandom;

/**
 * <p>
 * 昵称生成工具类
 * </p>
 *
 * @author weixin
 * @date 2023/3/27 11:53
 */
@Slf4j
@Component
public class NicknameGenerator {

    private static final int NAME_LENGTH = 3;
    private static final int NUMBER_LENGTH = 3;
    private static final String NAME_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String NUMBER_CHARS = "0123456789";
    private static final int MAX_RETRIES = 10;

    private final RedissonClient redissonClient;

    private final long maxNumber;
    private final String maxNumberString;

    public NicknameGenerator(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
        this.maxNumber = (long) Math.pow(NUMBER_CHARS.length(), NUMBER_LENGTH);
        this.maxNumberString = String.format("%0" + NUMBER_LENGTH + "d", maxNumber - 1);
    }

    public String generateNickname() {
        StringBuilder sb = new StringBuilder();

        // generate random name
        for (int i = 0; i < NAME_LENGTH; i++) {
            int index = ThreadLocalRandom.current().nextInt(NAME_CHARS.length());
            sb.append(NAME_CHARS.charAt(index));
        }

        // generate random number
        long number = ThreadLocalRandom.current().nextLong(maxNumber);
        String numberStr = String.format("%0" + NUMBER_LENGTH + "d", number);
        sb.append(numberStr);

        // check if nickname exists in Redis
        String nickname = sb.toString();
        RBucket<Boolean> bucket = redissonClient.getBucket(nickname);
        int retries = 0;
        while (bucket.isExists() && retries < MAX_RETRIES) {
            // update the nickname with a new number
            log.error(nickname + "昵称已存在，循环次数：" + retries);
            nickname = updateNicknameWithNewNumber(sb, number);
            bucket = redissonClient.getBucket(nickname);
            retries++;
        }
        if (bucket.isExists()) {
            log.error("昵称已存在");
            throw new RuntimeException("Failed to generate unique nickname after " + MAX_RETRIES + " retries.");
        }

        // save nickname to Redis
        bucket.set(true);

        return nickname;
    }

    private String updateNicknameWithNewNumber(StringBuilder sb, long oldNumber) {
        // generate new number
        long number = ThreadLocalRandom.current().nextLong(maxNumber);

        // check if new number is the same as old number
        if (number == oldNumber) {
            number = (number + 1) % maxNumber;
        }

        // update the number in the nickname
        String numberStr = String.format("%0" + NUMBER_LENGTH + "d", number);
        sb.setLength(NAME_LENGTH);
        sb.append(numberStr);

        // check if new nickname exceeds max number
        if (sb.substring(NAME_LENGTH).equals(maxNumberString)) {
            // reset the number to 0 and increment the name
            sb.setLength(NAME_LENGTH);
            for (int i = NAME_LENGTH - 1; i >= 0; i--) {
                char c = sb.charAt(i);
                int index = NAME_CHARS.indexOf(c);
                if (index == NAME_CHARS.length() - 1) {
                    sb.setCharAt(i, NAME_CHARS.charAt(0));
                } else {
                    sb.setCharAt(i, NAME_CHARS.charAt(index + 1));
                    break;
                }
            }
            sb.append("000");
        }

        return sb.toString();
    }

}

